// AppletTst1.java

/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**/

package	com.ibm.tspaces.examples.simple;

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.net.*;
import com.ibm.tspaces.*;
/**
** This class implements a simple Applet that 
** interfaces with TSpaces.  
** <pre>
** Some things to note:
**   - It ensures that only one instance of the applet is 
**     is running.  
**   - It uses the destroy() interface to cleanup the connection 
**     to the server.
**
** </pre>
** @version $Revision: 1.1.2.2 $ $Date: 2000/02/05 14:50:43 $
** @author <a href="mailto:jthomas@cruzio.com">John Thomas</a>

*/
public class AppletTst1 extends Applet  
             implements Callback, Runnable{

	
  /**
  ** true if running as an application
  */
 	private static boolean application = false;
  
  /**
  ** Number of instances of this class under this JVM
  */
  private static int   _instanceCount = 0;             
  /**
  ** This will be set true when start() called and 
  ** set to false when stop() is called;
  */
  protected static boolean _started = false;
    
  protected static final int WIDTH = 240;
  protected static final int HEIGHT = 300;
  
  
	public Frame 	appFrame;
	
	
	/**
  ** This holds a reference to the current TupleSpace.
  */
  protected TupleSpace _ts = null;
  protected static Color BACKGROUND_COLOR = Color.white;

  /** 
  ** This is the Host where the TupleSpace Server is running
  */
  protected static String _host = TupleSpace.DEFAULTHOST;
  protected static int _port = TupleSpace.DEFAULTPORT;
  /**
  ** The Space name
  */
  protected static String _tsName = "AppletTst1";
  
  private boolean _failed = false;             
  String _msg = "Unknown error";
  
  /**
  ** Sequence number assigned to EventRegister
  */
  private int _seqNum = -1;
/*
***************************************************************************
** ** constructor **
** **********
*/
/**
** The default constructor.
**
** The constructor has little use in an applet but 
** we have one just for completeness.
**
**
*************************************************************************** 
*/
               
public 
AppletTst1() {
		Debug.out("Constructor entered");
}
               
               
/*
***************************************************************************
** ** init **
** **********
*/
/**
** Initialization: 
** This could be entered automatically when running as an applet 
** or by being called from main() is being run from an 
** application.
**
** This is where all of the window and TSpace setup is done
** Note that we increment a static variable (_instanceCount) to
** keep track of how many instances of this applet are running 
** 
*************************************************************************** 
*/
public void init() {
  Debug.setDebugOn(true);
  TupleSpace.setDebug(true);
  Debug.out("AppletTst1 init() ");
  
  if (application) {                 // Running as an application
    
    appFrame = new Frame("TSpaces Applet Test");
    appFrame.add("Center", this);
    appFrame.setSize(WIDTH,HEIGHT);
  } else  {    // Running as an applet
    Component c = this.getParent();
    while (c != null && ! (c instanceof Frame)) 
      c = c.getParent();
    
    appFrame = (Frame)c;
    Debug.out("appFrame="+appFrame);
    
      
    // We will assume that Tuplespace is on same host as applet codebase
    URL url = getCodeBase();
    String urlhost = url.getHost();
    Debug.out("Host="+urlhost);
    if (urlhost.length() != 0) 
        _host = urlhost;
  
  }
  
  this.setLayout(new BorderLayout());
  
  if (_instanceCount > 0 ) {
    _failed = true;
    _msg = "Applet already active. Only one allowed";
  } else {
    _instanceCount++;      // keep track of how many are started
    setupButtons(this);
  }
  if (_failed) {
    Label err = new Label(_msg);
    this.add("South",err);
    appFrame.show();
    return;
  }
        
  setBackground(BACKGROUND_COLOR);
  
  
  // Handle the user exiting/killing the application
  // I don't think this can happen with an applet.
  if ( application && appFrame != null ) {
    appFrame.addWindowListener( new WindowAdapter()  {
      public void 
      windowClosing (WindowEvent e) {
        Debug.out("Window Close event");
        destroy();
        System.exit(0);
      }
    } );
  }
    
  Debug.out("Access tuplespace at " + _host);
  String user = TupleSpace._DEFAULT_USER;
  String pass = TupleSpace._DEFAULT_PASSWORD;
  //  Tell the TupleSpace code that we are going to be running 
   //  in an applet sandbox and to avoid any otion that is 
   //  invalid.
  TupleSpace.appletEnvironment(true);
  String version = TupleSpace.getVersion();
  Debug.out("TSpaces Version="+version);
  
  try {
    // Verify that the Server is active
    Tuple active = TupleSpace.status(_host,_port);
    Debug.out("Status="+active);
      // status() returns a simple Tuple that contains "Running" or "NotRunning"
    if ( active == null || 
         active.getField(0).getValue().equals("NotRunning") ) { 
      _msg = "TSpaces server not running on " + _host;
      Debug.out(_msg);
      _failed = true;
    }
  } catch (TupleSpaceException tse) {
    _failed = true;
    _msg = tse.getMessage();
    Debug.out(tse);
  }
  TupleSpace.setConnectionTries(1);
  if (!_failed) {
    _failed = ! setupTS();
    
  }
  if (_failed ) {
    Label errmsg = new Label("Failure: "+ _msg);
    appFrame.add("South",errmsg);
      
    appFrame.setVisible(true);      
    return;
  }
  appFrame.setVisible(true);

} // init
	
	
	  /**************************************************************************
    ** ** setupButtons **
    ** **********
    */
    /**
    ** sets up some buttons that user can poke at 
    ** Currently it only creates a "Write" button that 
    ** causes a tuple to be written.
    **
    *************************************************************************** 
    */
   

	
	public void
	setupButtons(Panel appPanel) {
    appPanel.setLayout(new BorderLayout());
		Debug.out("setupButtons()");
		Button writeButton = new Button("WRITE");
		writeButton.addActionListener(
			new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					
					try {
            // May have done destroy in another window
            if (_ts == null) 
              setupTS();
            if (_ts != null) {              
              _ts.write("Write it");
              Debug.out("Write it");
            } else {
              Debug.out("Write bypassed, _ts == null");
              
            }
          } catch (TupleSpaceException tse) {
            Debug.out(tse.getMessage());
            _ts = null;
					} catch (Exception ex) {
						Debug.out(ex);
					}
				}
			} );
		appPanel.add("Center",writeButton); 
		
    return;
	}
	
 
    /**************************************************************************
    ** ** setupTS **
    ** **********
    */
    /**
    ** setup for TSpaces access 
    ** 
    ** @return true if the setup is OK
    *************************************************************************** 
    */
   

  
  public boolean
  setupTS() {
    
    Debug.out("setupTS()");

    try {
  
      //--------------------------------------------------
      // Setup to access TupleSpace
      // We access the  tuplespace on the indicatd host.
      //
      // We then just register for the write event 
      //   we must implement Callback which will the call() method to
      //   be called anytime anyone writes a matching tuple to the tuplespace 
      //------------------------------------------------------------
      
   
      Debug.out("_ts="+_ts);
      _ts = new TupleSpace(_tsName, _host);
  
      // create a template to indicate what we are interested in
      Tuple match = new Tuple(); 
             
      _seqNum = _ts.eventRegister(TupleSpace.WRITE,match, this);
      Debug.out("EventRegister() seqNum="+_seqNum);
      //_ts.write("TestWrite");
      
    } catch(TupleSpaceException tse) {
      Debug.out(tse);         
      _msg = tse.getMessage();
      return false;
    } 
    return true;
  } // setupTS
                   
               
               
	/*
    ***************************************************************************
    ** ** run **
    ** **********
    */
    /**
    ** run: Is entered as as a result of a start thread 
    ** It is not currently being used.
    **
    *************************************************************************** 
    */
   

	
	public void
	run() {
		Debug.out("run()");
		while (true) {
			try {
				Thread.currentThread().sleep(5000);
		  } catch (InterruptedException ie) {}
    }
	}
	
  /*
    ***************************************************************************
    ** ** paint **
    ** **********
    */
    /**
    ** paint() is called to redisplay 
    **
    
    **
    *************************************************************************** 
    */
    public void 
    paint(Graphics g)   {
    Debug.out("paint()");
    
  }

  
	/*
    ***************************************************************************
    ** ** start **
    ** **********
    */
    /**
    ** start() is called when it is time for the applet to start doing stuff.
    **
    ** This could be called multiple times as the applet is stopped and started. 
    ** If the TupleSpace setup has been terminated, then it will be 
    ** restarted.  
    **
    *************************************************************************** 
    */
    public void 
    start()  {
		Debug.out("start()");
		_started = true;
    if (_ts == null && !_failed ) {
      setupTS();
    }
	}
	
	/*
    ***************************************************************************
    ** ** stop **
    ** **********
    */
    /**
    ** stop() is called when the applet is no longer visible so we no longer need to 
    ** keep painting the screen, but we should still listen for events.
    **
    ** This could be called multiple times as the applet is stopped and started. 
    **
    ** Since the startup will reinit the TupleSpace environment,
    ** we can issue a cleanup() here but only if there are 
    ** no other active instances.
    *************************************************************************** 
    */
    public void 
    stop()  {
		Debug.out("stop()");
		_started = false;
  /****    
    if (_instanceCount == 1) {
      TupleSpace.cleanup(_ts.getServer(),_ts.getPort());
      _ts = null;
      Debug.out("TupleSpace connections terminated");
    }
  ****/
	}
	
	/*
  ***************************************************************************
  ** ** destroy **
  ** **********
  */
  /**
  ** destroy() is called when the applet is going to be permanently stoped
  **
  ** We will decrement the instance count and if it is now zero
  ** we will cleanup the TSpaces connection.
  *************************************************************************** 
  */
  public void 
  destroy()  {
		Debug.out("destroy()");
    
    if (_failed) 
      return;
    
    _instanceCount--;    // This instance will disappear.
    if (_instanceCount == 0) {
      try {
        _ts.eventDeRegister(_seqNum);
        Debug.out("events deregistered: "+_seqNum);
      } catch (TupleSpaceException tse) {
        Debug.out(tse);
      }
      TupleSpace.cleanup(_ts.getServer(),_ts.getPort());
      _ts = null;
      Debug.out("cleanup done");
    } else { 
      Debug.out("cleanup() bypassed");
    }
	
	}
               
               
 /*
    ***************************************************************************
    ** ** call **
    ** **********
    */
    /**
    ** Process the callback from the server that notifies us when anyone 
    ** (including ourselves) writes to the TupleSpace.
    ** 
    ** @param eventName_ the name of the event command that caused this call, that is the
    ** name of the client side command of the thread that registered this call, e.g., in the
    ** case of a read, this would be TupleSpace.READ, **not** TupleSpace.WRITE which is the
    ** corresponding command that caused the actaul event.
    ** @param tsName_ the name of the tuple space this command was executed on.
    ** @param sequenceNumber_ the sequenceNumber for this event/command
    ** @param tuple_ the returned tuple or a Tuple with an exception inside
    ** @param isException_ was the command processed normaly or was there an exception
    **
    ** @return true if this is the last call this sequence # should be getting otherwise false
    *************************************************************************** 
    */
    public synchronized boolean 
    call(String eventName_, String tsName_, int sequenceNumber_, SuperTuple tuple_, 
                    boolean isException_) {
               
      try { 
	    	Debug.out("callback for " + eventName_ 
	       + ", TupleSpace = " + tsName_  
	       + ",Seq #=" + sequenceNumber_  
	       + ",Tuple="  +   tuple_ 
	       + ", isException=" + isException_);
	  
				if (isException_) {
					Throwable t = (Throwable)tuple_.getField(0).getValue();
					Debug.out(0,t);
          return true;
				}
    
     }  catch (Exception e) {
      	Debug.out(e);
     }
      
      return false;
    
  } // call

	
    /*
    ***************************************************************************
    ** ** main **
    ** **********
    */
    /**
    ** Run the AppletTst1 as an application.
    **
    ** @param args - command line arguments
    *************************************************************************** 
    */
    public static void main(String args[]) {
      
      AppletTst1 appletTst1;

      try {
        Debug.setDebugOn(true);
        application = true;
        if(args.length >= 1)
          _host = args[0];      
  
        appletTst1 = new AppletTst1();
        
  
        appletTst1.init();
        
        appletTst1.start();
        Thread.sleep(1000); 
        appletTst1.stop();
        Thread.sleep(5000);
        appletTst1.destroy();
     } catch (Exception e) {
       Debug.out(e);
     }
         
    } // main
} // AppletTst1

/*
$Log: AppletTst1.java,v $
Revision 1.1.2.2  2000/02/05 14:50:43  jthomas
no message

Revision 1.1.2.1  2000/01/28 18:17:59  jthomas
New applet example

Revision 1.1.2.2  2000/01/24 20:33:20  jthomas
changed so that it can handle multiple instances in the same browser

Revision 1.1.2.1  2000/01/17 17:10:47  jthomas
no message

/*
*/